﻿///@author Rafał Hazan
///@email  rafal.hazan@gmail.com
#ifndef __GENOTYPE__
#define __GENOTYPE__

#include "MatrixGraph.h"

#include <vector>
#include <set>
#include <iostream>

///Gennotyp, a zarazem Fenotyp (ktory sklada sie z jednego genotypu).
///Genotyp reprezentuje pewien podgraf, tutaj w szczegolnosci klike.
class Genotype
{
	friend std::ostream & operator<<(std::ostream & output, const Genotype & p);

public:
	///graf, do ktorego genotyp odnosi sie
	const MatrixGraph & graph;
	///ustawione bity genotypu
	std::set<int> gens;
	///wartosc przystosowania genotypu, dla zwiekszenia wydajnosci algorytmu, jest obliczana po kazdej zmianie genotypu
	double fitnessValue;

	Genotype(const MatrixGraph & g)
		: graph(g), fitnessValue(0)
	{
	}

	Genotype(const Genotype & g2)
		: graph(g2.graph)
	{
		this->gens = g2.gens;
		this->fitnessValue = g2.fitnessValue;
	}

	/// @return true jesli wierzcholek o danym indeksie nalezy do podgrafu, ktory reprezentuje genotyp
    bool isOne(int vertIndex) const
    {
        return gens.count(vertIndex) > 0;
    }

	/// @return true jesli wierzcholek o danym indeksie nie nalezy do podgrafu, ktory reprezentuje genotyp
    bool isZero(int vertIndex) const
    {
        return gens.count(vertIndex) == 0;
    }

	///@return wielkosc podgrafu
    int getVSize() const
    {
        return gens.size();
    }

	///@return zbior wierzcholkow, ktore nie naleza do danego podgrafu
	std::set<int> getZeros() const
	{
		std::set<int> result;
        for (unsigned int i = 0; i < graph.getVSize(); ++i)
		{
			if (isZero(i))
			{
				result.insert(i);
			}
		}
		return result;
	}

	/// zwraca zbior wierzcholkow, ktore sa w gen ale nie ma ich tym obiekcie
	std::set<int> getNotContained(Genotype & gen)
	{
        std::set<int> result(gen.gens);
        for (std::set<int>::const_iterator iter = this->gens.begin(); iter != this->gens.end(); ++iter)
        {
            if (result.count(*iter) > 0)
            {
                result.erase(*iter);
            }
        }
        return result;
	}

	///@return true jesli wierzcholek o indeksie v jest polaczany ze wszystkimi wierzcholkami podgrafu.
	bool isConnectedToAll(int v) const
	{
		bool result = true;
        for(std::set<int>::const_iterator iter = gens.begin(); iter != gens.end(); ++iter)
        {
            if ( !graph.areConnectedVerts(v, *iter) )
            {
                result = false;
                break;
            }
        }
		return result;
	}

	/// Ustawia bit o indeksie i (dodaje wiercholek i do podgrafu).
	void set(int i)
	{
        gens.insert(i);
	}

	/// Uaktualnia wartosc funkcji przystosowania. Dla zszybszenia działania.
	void recalculateFitnessValue()
	{
		fitnessValue = evaluate(graph);
	}

	/// @return true jesli podgraf zawiera wszystkie wierzcholki 'nadgrafu'
	bool containsAllGraphVerts() const
	{
        return graph.getVSize() == gens.size();
	}

	bool operator==(const Genotype & otherGenotype) const
	{
		return gens == otherGenotype.gens;
	}

	void operator=(const Genotype & otherGenotype)
	{
		gens = otherGenotype.gens;
		fitnessValue = otherGenotype.fitnessValue;
	}

	/// Zwraca wynik wzgledem funkcji przystosowania.
	bool operator>(const Genotype & otherGenotype) const
	{
		//double v1 = evaluate(graph);
		//double v2 = otherGenotype.evaluate(graph);
		if (fitnessValue == otherGenotype.fitnessValue)
		{
			return gens < otherGenotype.gens;
		}
		else
		{
			return fitnessValue < otherGenotype.fitnessValue; // znak < ponieważ chcemy ciąg roznący.
		}
	}

	/// Zwraca wynik wzgledem funkcji przystosowania.
	bool operator<(const Genotype & otherGenotype) const
	{
		//double v1 = evaluate(graph);
		//double v2 = otherGenotype.evaluate(graph);
		if (fitnessValue == otherGenotype.fitnessValue)
		{
			return gens > otherGenotype.gens;
		}
		else
		{
			return fitnessValue > otherGenotype.fitnessValue;
		}
	}

private:
	///Oblicza wartosc funkcji przystosowania dla opdgrafu.
	double evaluate(const MatrixGraph & graph) const
	{
		int bridgeEdges = 0;
		for (std::set<int>::iterator i = gens.begin(); i != gens.end(); ++i)
		{
			for (unsigned int j = 0; j < graph.getVSize(); ++j)
			{
				if (graph.areConnectedVerts(*i, j) && isZero(j) )
				{
					++bridgeEdges;
				}
			}
		}
		//std::cout << "fittness " << *this << " " << (gens.size() * graph.getESize() + bridgeEdges) << std::endl;
		return gens.size() * graph.getESize() + bridgeEdges;
	}
};

#endif